package com.cloudkinto.service.order.shelf.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cloudkinto.bo.api.InboundReceiptAndShelfApiBo;
import com.cloudkinto.bo.order.shelf.*;
import com.cloudkinto.bo.storage.StorageLocationQueryBo;
import com.cloudkinto.common.BeanConvert;
import com.cloudkinto.common.DynamicTablePolicy;
import com.cloudkinto.common.constant.SysConstant;
import com.cloudkinto.common.exception.BizException;
import com.cloudkinto.common.utils.dict.StaticDict;
import com.cloudkinto.common.web.PageResult;
import com.cloudkinto.common.web.SingleResult;
import com.cloudkinto.dao.OrderInputDao;
import com.cloudkinto.dao.OrderInputShelfDao;
import com.cloudkinto.dto.print.NormalShelfTableDto;
import com.cloudkinto.dto.print.TransferShelfTableDto;
import com.cloudkinto.dto.print.common.DynamicTableDto;
import com.cloudkinto.dto.stock.NormalInputShelfDto;
import com.cloudkinto.dto.stock.TransferInputShelfDto;
import com.cloudkinto.entity.*;
import com.cloudkinto.enumType.common.ProgressEnum;
import com.cloudkinto.enumType.operate.OperateTypeEnum;
import com.cloudkinto.enumType.order.inbound.InboundTypeEnum;
import com.cloudkinto.enumType.order.inbound.OrderInputStatusEnum;
import com.cloudkinto.enumType.order.shelf.OrderInputShelfStatusEnum;
import com.cloudkinto.enumType.stock.StockOrderTypeEnum;
import com.cloudkinto.enumType.stock.StockTypeEnum;
import com.cloudkinto.enumType.storage.StorageAreaEnum;
import com.cloudkinto.service.company.CompanyService;
import com.cloudkinto.service.event.order.InboundAddCostEvent;
import com.cloudkinto.service.operate.OperateRecordService;
import com.cloudkinto.service.order.box.OrderBoxProductService;
import com.cloudkinto.service.order.box.OrderBoxService;
import com.cloudkinto.service.order.input.OrderInputService;
import com.cloudkinto.service.order.shelf.OrderInputShelfService;
import com.cloudkinto.service.product.ProductService;
import com.cloudkinto.service.progress.ProgressService;
import com.cloudkinto.service.stock.ProductStockFlowingService;
import com.cloudkinto.service.stock.ProductStockRackService;
import com.cloudkinto.service.stock.ProductStockService;
import com.cloudkinto.service.storage.StorageLocationService;
import com.cloudkinto.service.storage.impl.StorageLocationServiceImpl;
import com.cloudkinto.service.wr.QuotationSchemeService;
import com.cloudkinto.utils.AsposePdfUtils;
import com.cloudkinto.utils.BarCodeUtils;
import com.cloudkinto.vo.common.ListPageStatusStaDto;
import com.cloudkinto.vo.cost.BoxMsg;
import com.cloudkinto.vo.cost.Size;
import com.cloudkinto.vo.cost.SkuMsg;
import com.cloudkinto.vo.order.shelf.*;
import com.cloudkinto.vo.stock.ProductFlowingInEt;
import com.cloudkinto.vo.storage.StorageLocationVo;
import com.cloudkinto.vo.wr.cost.OrderInputCostReq;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.config.ConfigureBuilder;
import com.deepoove.poi.data.*;
import com.deepoove.poi.util.PoitlIOUtils;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.RequiredArgsConstructor;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * <p>
 * 入库产品上架表（中间表） service实现类
 * </p>
 *
 * @author long
 * @since 2024-11-07
 */
@Service
@Transactional(rollbackFor = Exception.class)
@RequiredArgsConstructor
public class OrderInputShelfServiceImpl extends ServiceImpl<OrderInputShelfDao, OrderInputShelfDo> implements OrderInputShelfService {

    private final CompanyService companyService;
    private final OrderBoxService boxService;
    private final OrderBoxProductService boxProductService;
    private final ProductStockService productStockService;
    private final ProductStockRackService productStockRackService;
    private final ProductService productService;
    private final StorageLocationService storageLocationService;
    private final OperateRecordService operateRecordService;
    private final OrderInputDao orderInputDao;
    private final ProgressService progressService;
    private final QuotationSchemeService quotationSchemeService;
    private final ThreadPoolTaskExecutor taskExecutor;
    private final ProductStockFlowingService stockFlowingService;
    private final RabbitTemplate rabbitTemplate;
    private final ApplicationContext context;
    @Lazy
    @Autowired
    private OrderInputService orderInputService;

    @Override
    public OrderInputShelfDetailVo detail(Long id, Long tenantId) {
        OrderInputShelfDo shelfDo = baseMapper.selectById(id);
//        OrderInputShelfDetailVo vo = new OrderInputShelfDetailVo();
//        BeanUtils.copyProperties(shelfDo, vo);
        OrderInputShelfDetailVo vo = BeanConvert.INSTANCE.shelfDoToShelfDetailVo(shelfDo);
        return handlerDetailAfter(vo, tenantId, vo.getCompanyId());
    }

    /**
     * 查询明细之后要执行的业务定制处理
     */
    private OrderInputShelfDetailVo handlerDetailAfter(OrderInputShelfDetailVo vo, Long tenantId, Long companyId) {
        Long pasId = vo.getId();
        //上架单号
        String pasNo = vo.getPasNo();
        List<OrderBoxDo> boxList = boxService.lambdaQuery()
                .eq(OrderBoxDo::getPasNo, pasNo)
//                .isNotNull(OrderBoxDo::getReceivedTime)
                .list();
        //入库类型
        Integer inboundType = vo.getInboundType();
        if (InboundTypeEnum.one.getValue().equals(inboundType)) {
            //箱唛号
            List<String> boxMarkNumberList = boxList.stream()
                    .map(OrderBoxDo::getBoxMarkNumber)
                    .collect(Collectors.toList());
            //查询箱内产品
            List<OrderBoxProductDo> bpList = boxProductService.lambdaQuery()
                    .in(OrderBoxProductDo::getBoxMarkNumber, boxMarkNumberList)
                    .ne(OrderBoxProductDo::getReceiptQty, 0)
                    .list();
            Map<Long, OrderBoxProductDo> bpMap = bpList.stream().collect(Collectors.toMap(OrderBoxProductDo::getProductId, val -> val, (o1, o2) -> {
                o1.setReceiptQty(o1.getReceiptQty() + o2.getReceiptQty());
                return o1;
            }));
            //查询产品
            Map<Long, ProductDo> productIdMatchMap = productService.lambdaQuery()
                    .in(ProductDo::getId, bpMap.keySet())
                    .list().stream().collect(Collectors.toMap(ProductDo::getId, val -> val));
            List<NormalSkuVo> normalSkuVo = new ArrayList<>();
            List<NormalCellVo> normalCellVo = new ArrayList<>();
            //查询上架数据
            Map<Long, List<NormalInputShelfDto>> productIdMatchShelfList = productStockService.getNormalInputShelfData(Collections.singletonList(pasId))
                    .stream().collect(Collectors.groupingBy(NormalInputShelfDto::getProductId));
            //产品信息补充vo类
            for (Map.Entry<Long, OrderBoxProductDo> entry : bpMap.entrySet()) {
                Long productId = entry.getKey();
                OrderBoxProductDo bp = entry.getValue();
                List<NormalInputShelfDto> normalInputShelfDtoList = productIdMatchShelfList.get(productId);
                ProductDo productDo = productIdMatchMap.get(productId);
                if (Objects.isNull(productDo)) {
                    continue;
                }
                normalSkuVo.add(
                        NormalSkuVo.builder()
                                .id(bp.getId())
                                .sku(productDo.getSku())
                                .mainBarcode(productDo.getJanCode())
                                .productName(productDo.getProductName())
                                .shouldShelfBoxQty(bp.getReceiptQty())
                                .actualShelfBoxQty(
                                        CollUtil.isEmpty(normalInputShelfDtoList) ? 0 :
                                                normalInputShelfDtoList.stream()
                                                        .filter(item -> Objects.nonNull(item.getStockAttribute()))
                                                        .map(NormalInputShelfDto::getReceiptQty)
                                                        .reduce(0, Integer::sum)
                                )
                                .goodQty(
                                        CollUtil.isEmpty(normalInputShelfDtoList) ? 0 :
                                                normalInputShelfDtoList.stream()
                                                        .filter(item -> Objects.nonNull(item.getStockAttribute()) && item.getStockAttribute().equals(0))
                                                        .map(NormalInputShelfDto::getReceiptQty)
                                                        .reduce(0, Integer::sum)
                                )
                                .badQty(
                                        CollUtil.isEmpty(normalInputShelfDtoList) ? 0 :
                                                normalInputShelfDtoList.stream()
                                                        .filter(item -> Objects.nonNull(item.getStockAttribute()) && item.getStockAttribute().equals(1))
                                                        .map(NormalInputShelfDto::getReceiptQty)
                                                        .reduce(0, Integer::sum)
                                )
                                .build()
                );
                if (CollUtil.isNotEmpty(normalInputShelfDtoList)) {
                    normalInputShelfDtoList.forEach(
                            item -> normalCellVo.add(
                                    NormalCellVo.builder()
                                            .id(bp.getId())
                                            .sku(item.getSku())
                                            .mainBarcode(productDo.getJanCode())
                                            .productName(productDo.getProductName())
                                            .actualShelfBoxQty(item.getReceiptQty())
                                            .storageLocationCode(item.getCellCode())
                                            .batchNo("BN" + DateUtil.format(item.getCreateTime(), "yyyyMMddHHmm"))
                                            .build()
                            )
                    );
                }
            }
            vo.setNormalSkuVo(normalSkuVo);
            vo.setNormalCellVo(normalCellVo);
        } else if (InboundTypeEnum.two.getValue().equals(inboundType)) {
            //箱类号
            List<String> boxTypeNumberList = boxList.stream()
                    .map(OrderBoxDo::getBoxTypeNumber)
                    .collect(Collectors.toList());
            //查询箱内产品
            List<OrderBoxProductDo> bpList = boxProductService.lambdaQuery()
                    .in(OrderBoxProductDo::getBoxTypeNumber, boxTypeNumberList)
                    .list();
            //查询所有涉及到的产品
            Map<Long, ProductDo> productIdMatch = productService.lambdaQuery()
                    .in(ProductDo::getId, bpList.stream().map(OrderBoxProductDo::getProductId).collect(Collectors.toSet()))
                    .list().stream().collect(Collectors.toMap(ProductDo::getId, val -> val));
            //根据箱类号进行分组箱内产品
            Map<String, List<OrderBoxProductDo>> boxTypeNumberMatchList = bpList.stream()
                    .collect(Collectors.groupingBy(OrderBoxProductDo::getBoxTypeNumber));
            //查询上架数据
            Map<String, List<TransferInputShelfDto>> boxTypeNumberMatchShelfList = productStockService.getTransferInputShelfData(Collections.singletonList(pasId))
                    .stream().collect(Collectors.groupingBy(TransferInputShelfDto::getBoxTypeNumber));
            //组装vo数据
            List<TransferBoxTypeVo> transferBoxTypeVo = new ArrayList<>();
            List<TransferCellVo> transferCellVo = new ArrayList<>();
            for (OrderBoxDo box : boxList) {
                List<TransferBoxTypeSkuVo> skuList = new ArrayList<>();
                for (OrderBoxProductDo bp : boxTypeNumberMatchList.get(box.getBoxTypeNumber())) {
                    ProductDo productDo = productIdMatch.get(bp.getProductId());
                    if (productDo == null) {
                        continue;
                    }
                    skuList.add(
                            TransferBoxTypeSkuVo.builder()
                                    .id(bp.getId())
                                    .sku(bp.getSku())
                                    .mainBarcode(productDo.getJanCode())
                                    .productName(productDo.getProductName())
                                    .perBoxQty(bp.getReceiptQty())
                                    .build()
                    );
                }
                List<TransferInputShelfDto> shelfDtos = boxTypeNumberMatchShelfList.get(box.getBoxTypeNumber());
                transferBoxTypeVo.add(
                        TransferBoxTypeVo.builder()
                                .id(box.getId())
                                .boxTypeNumber(box.getBoxTypeNumber())
                                .customizeBarcode(box.getCustomizeBarcode())
                                .boxSkuQty(box.getReceivedQty())
                                .shouldShelfBoxQty(box.getReceivedBoxQty())
                                .actualShelfBoxQty(
                                        CollUtil.isNotEmpty(shelfDtos)
                                                ? shelfDtos.stream()
                                                .mapToInt(TransferInputShelfDto::getReceiptQty)
                                                .sum()
                                                : 0
                                )
                                .skuList(skuList)
                                .build()
                );
                if (CollUtil.isNotEmpty(shelfDtos)) {
                    shelfDtos.forEach(shelf ->
                            transferCellVo.add(
                                    TransferCellVo.builder()
                                            .id(box.getId())
                                            .boxTypeNumber(box.getBoxTypeNumber())
                                            .customizeBarcode(box.getCustomizeBarcode())
                                            .batchNo("BN" + DateUtil.format(shelf.getCreateTime(), "yyyyMMddHHmm"))
                                            .shelfBoxQty(shelf.getReceiptQty())
                                            .storageLocationCode(shelf.getCellCode())
                                            .build()
                            )
                    );
                }
            }
            vo.setTransferBoxTypeVo(transferBoxTypeVo);
            vo.setTransferCellVo(transferCellVo);
        }
        return vo;
    }

    @Override
    public SingleResult<Map<String, Object>> pageInit(Long userId, Long tenantId, String source) {
        HashMap<String, Object> map = new HashMap<>(8);
        map.put("statusList", OrderInputShelfStatusEnum.getOrderInputShelfStatusList());
        map.put("inboundType", InboundTypeEnum.getInboundTypeList());
        map.put("companyList", companyService.getCompanyList(userId, tenantId, source));
        return SingleResult.success(map);
    }

    @Override
    public PageResult<OrderInputShelfListVo> getListPage(OrderInputShelfQueryBo bo) {
        Integer current = bo.getCurrent();
        Integer pageSize = bo.getPageSize();
        PageHelper.startPage(current, pageSize);
        List<OrderInputShelfListVo> list = baseMapper.getListPage(bo);
        PageInfo<OrderInputShelfListVo> pageInfo = new PageInfo<>(list);
        return new PageResult<>(
                this.handlerListAfter(list),
                current,
                pageSize,
                (int) pageInfo.getTotal(),
                pageInfo.getPages()
        );
    }

    /**
     * 查询分页列表之后对列表数据的处理
     */
    private List<OrderInputShelfListVo> handlerListAfter(List<OrderInputShelfListVo> list) {
        if (CollUtil.isNotEmpty(list)) {
            List<OrderBoxDo> boxList = boxService.lambdaQuery()
                    .in(
                            OrderBoxDo::getPasNo,
                            list.stream()
                                    .map(OrderInputShelfListVo::getPasNo)
                                    .collect(Collectors.toList())
                    ).list();
            //根据pasNo分组
            Map<String, List<OrderBoxDo>> pasNoGetBoxList = boxList.stream()
                    .collect(Collectors.groupingBy(OrderBoxDo::getPasNo));
            List<OrderBoxProductDo> bpList = boxProductService.lambdaQuery()
                    .in(
                            OrderBoxProductDo::getBoxMarkNumber,
                            boxList.stream()
                                    .map(OrderBoxDo::getBoxMarkNumber)
                                    .collect(Collectors.toList())
                    )
                    .or()
                    .in(
                            OrderBoxProductDo::getBoxTypeNumber,
                            boxList.stream()
                                    .map(OrderBoxDo::getBoxTypeNumber)
                                    .collect(Collectors.toList())
                    )
                    .list();
            list.forEach(dto -> {
                List<OrderBoxDo> boxListByPasNo = pasNoGetBoxList.getOrDefault(dto.getPasNo(), new ArrayList<>());
                //计算产品数量
                dto.setSkuTotalQty(boxListByPasNo.stream().mapToInt(OrderBoxDo::getReceivedQty).sum());
                Collection<OrderBoxProductDo> values = bpList.stream()
                        .filter(i -> boxListByPasNo.stream()
                                .anyMatch(bp -> {
                                    if (InboundTypeEnum.one.getValue().equals(dto.getInboundType())) {
                                        return Objects.equals(bp.getBoxMarkNumber(), i.getBoxMarkNumber());
                                    }
                                    if (InboundTypeEnum.two.getValue().equals(dto.getInboundType())) {
                                        return Objects.equals(bp.getBoxTypeNumber(), i.getBoxTypeNumber());
                                    }
                                    return false;
                                })
                        )
                        .collect(Collectors.toMap(OrderBoxProductDo::getSku, val -> val, (o1, o2) -> {
                            o1.setReceiptQty(o1.getReceiptQty() + o2.getReceiptQty());
                            return o1;
                        })).values();
                dto.setSkuList(
                        JSONUtil.toList(JSONUtil.toJsonStr(values), OrderInputShelfListSkuVo.class)
                );
            });
        }
        return list;
    }

    @Override
    public SingleResult<Map<String, Object>> shelfDetailInit(Long tenantId) {
        HashMap<String, Object> map = new HashMap<>(4);
        map.put("inboundType", InboundTypeEnum.getInboundTypeList());
        map.put("statusList", OrderInputShelfStatusEnum.getOrderInputShelfStatusList());
        map.put("storageAreaType", StorageAreaEnum.getMapList());
        map.put("storageLocationType", StorageLocationServiceImpl.storageLocationTypeMap);
        return SingleResult.success(map);
    }

    @Override
    public ShelfDetailVo shelfDetail(ShelfDetailQueryBo bo) {
        if (StrUtil.isBlank(bo.getPasNo())
            && StrUtil.isBlank(bo.getBoxMarkNumber())
            && StrUtil.isBlank(bo.getBoxTypeNumber())) {
            throw new BizException(SysConstant.Query_Condition_Empty);
        }
        ShelfDetailVo dto = baseMapper.shelfDetail(bo);
        if (Objects.isNull(dto)) {
            throw new BizException(SysConstant.Shelf_Not_Exist);
        }
        return dto;
    }

    @Override
    public PageResult<ShelfBySkuVo> shelfBySkuListPage(ShelfBySkuQueryBo bo) {
        Integer current = bo.getCurrent();
        Integer pageSize = bo.getPageSize();
        PageHelper.startPage(current, pageSize);
        List<ShelfBySkuVo> list = baseMapper.shelfBySkuListPage(
                bo,
                StockOrderTypeEnum.Shelf.getValue(),
                StockTypeEnum.one.getValue()
        );
        PageInfo<ShelfBySkuVo> pageInfo = new PageInfo<>(list);
        return new PageResult<>(
                list,
                current,
                pageSize,
                (int) pageInfo.getTotal(),
                pageInfo.getPages()
        );
    }

    @Override
    public PageResult<ShelfByBoxMarkNoVo> shelfByBoxMarkNoListPage(ShelfByBoxMarkNoQueryBo bo) {
        Integer current = bo.getCurrent();
        Integer pageSize = bo.getPageSize();
        PageHelper.startPage(current, pageSize);
        List<ShelfByBoxMarkNoVo> list = baseMapper.shelfByBoxMarkNoListPage(
                bo/*,
                StockOrderTypeEnum.Shelf.getValue(),
                StockTypeEnum.one.getValue()*/
        );
        PageInfo<ShelfByBoxMarkNoVo> pageInfo = new PageInfo<>(list);
        return new PageResult<>(
                this.shelfBoxListPageHandler(
                        list,
                        OrderBoxProductDo::getBoxMarkNumber,
                        ShelfByBoxMarkNoVo::getBoxMarkNumber
                ),
                current,
                pageSize,
                (int) pageInfo.getTotal(),
                pageInfo.getPages()
        );
    }

    @Override
    public PageResult<ShelfByBoxTypeNoVo> shelfByBoxTypeNoListPage(ShelfByBoxTypeNumberQueryBo bo) {
        Integer current = bo.getCurrent();
        Integer pageSize = bo.getPageSize();
        PageHelper.startPage(current, pageSize);
        List<ShelfByBoxTypeNoVo> list = baseMapper.shelfByBoxTypeNoListPage(
                bo,
                StockOrderTypeEnum.Shelf.getValue(),
                StockTypeEnum.two.getValue()
        );
        PageInfo<ShelfByBoxTypeNoVo> pageInfo = new PageInfo<>(list);
        return new PageResult<>(
                this.shelfBoxListPageHandler(
                        list,
                        OrderBoxProductDo::getBoxTypeNumber,
                        ShelfByBoxTypeNoVo::getBoxTypeNumber
                ),
                current,
                pageSize,
                (int) pageInfo.getTotal(),
                pageInfo.getPages()
        );
    }

    /**
     * 为箱子绑定sku列表
     *
     * @param list        数据
     * @param bpFunction  箱子内表字段
     * @param dtoFunction 数据内的字段
     * @param <T>         数据父类泛型
     */
    private <T extends BaseShelfVo> List<T> shelfBoxListPageHandler(List<T> list,
                                                                    SFunction<OrderBoxProductDo, String> bpFunction,
                                                                    SFunction<T, String> dtoFunction) {
        if (CollUtil.isNotEmpty(list)) {
            List<OrderBoxProductDo> bpList = boxProductService.lambdaQuery()
                    .in(bpFunction, list.stream().map(dtoFunction).collect(Collectors.toList())).list();
            //查询所有涉及到的产品
            Map<Long, ProductDo> productIdMatch = productService.lambdaQuery()
                    .in(ProductDo::getId, bpList.stream().map(OrderBoxProductDo::getProductId).collect(Collectors.toSet()))
                    .list().stream().collect(Collectors.toMap(ProductDo::getId, val -> val));
            Map<String, List<OrderBoxProductDo>> boxMatchData = bpList.stream()
                    .collect(Collectors.groupingBy(bpFunction));
            list.forEach(item -> {
                //使用函数提取实体中的对应字段值
                String field = dtoFunction.apply(item);
                List<OrderBoxProductDo> productDos = boxMatchData.get(field);
                List<ShelfPageListSkuVo> skuList = new ArrayList<>();
                for (OrderBoxProductDo bpDo : productDos) {
                    ProductDo productDo = productIdMatch.get(bpDo.getProductId());
                    skuList.add(
                            ShelfPageListSkuVo.builder()
                                    .sku(bpDo.getSku())
                                    .mainBarcode(Objects.nonNull(productDo) ? productDo.getJanCode() : "")
                                    .perBoxQty(bpDo.getReceiptQty())
                                    .build()
                    );
                }
                item.setSkuList(skuList);
            });
        }
        return list;
    }

    @Override
    public boolean shelfByBoxMarkNumber(ShelfByBoxMarkNumberAddBo bo, Long userId, String username) {
        //上架id&上架单号
        Long pasId = bo.getId();
        String pasNo = bo.getPasNo();
        List<ShelfByBoxMarkNumberAddBo.BoxMarkNumberDetail> detailList = bo.getDetailList();
        if (CollUtil.isEmpty(detailList)) {
            throw new BizException(SysConstant.Please_Select_Shelf_Data);
        }
        //查询上架单
        OrderInputShelfDo shelfDo = this.getById(pasId);
        //查询库位信息 用于匹配库存属性
        StorageLocationQueryBo slBo = new StorageLocationQueryBo();
        slBo.setIdList(detailList.stream().map(ShelfByBoxMarkNumberAddBo.BoxMarkNumberDetail::getCellId).collect(Collectors.toList()));
        slBo.setTenantId(shelfDo.getTenantId());
        slBo.setPageSize(1000);
        Map<Long, StorageLocationVo> cellIdMatchDo = storageLocationService.getListPage(slBo).getData()
                .stream().collect(Collectors.toMap(StorageLocationVo::getId, val -> val));
        //查询箱子产品信息
        Map<String, List<OrderBoxProductDo>> bpListMatch = boxProductService.lambdaQuery()
                .in(OrderBoxProductDo::getBoxMarkNumber, detailList.stream().map(ShelfByBoxMarkNumberAddBo.BoxMarkNumberDetail::getBoxMarkNumber).collect(Collectors.toList()))
                .list().stream().collect(Collectors.groupingBy(OrderBoxProductDo::getBoxMarkNumber));
        detailList.forEach(item -> {
            bpListMatch.get(item.getBoxMarkNumber()).forEach(bpDo -> {
                //组装数据插入主表
                ProductStockDo productStockDo = new ProductStockDo();
                productStockDo.setCompanyId(shelfDo.getCompanyId());
                productStockDo.setOrderType(StockOrderTypeEnum.Shelf.getValue());
                productStockDo.setOrderId(pasId);
                productStockDo.setType(StockTypeEnum.one.getValue());
                productStockDo.setProductId(bpDo.getProductId());
                productStockDo.setBoxMarkNumber(item.getBoxMarkNumber());
                productStockDo.setReceiveQuantity(bpDo.getReceiptQty());
                productStockService.save(productStockDo);
                //组装数据插入子表
                ProductStockRackDo productStockRackDo = new ProductStockRackDo();
                productStockRackDo.setStockId(productStockDo.getId());
                productStockRackDo.setRackId(item.getCellId());
                productStockRackDo.setStockAttribute(cellIdMatchDo.get(item.getCellId()).getMainType());
                productStockRackDo.setReceivedQty(bpDo.getReceiptQty());
                productStockRackDo.setQuantity(bpDo.getReceiptQty());
                productStockRackService.save(productStockRackDo);
                //库存流水
                stockFlowingService.insertProForIn(
                        ProductFlowingInEt.builder()
                                .sku(bpDo.getSku())
                                .companyId(shelfDo.getCompanyId())
                                .warehouseId(productStockDo.getWhId())
                                .rackId(item.getCellId())
                                .stockAttribute(cellIdMatchDo.get(item.getCellId()).getMainType())
                                .bookQuantity(bpDo.getReceiptQty())
                                .orderType(StockOrderTypeEnum.Shelf.getValue())
                                .noteNo(shelfDo.getPasNo())
                                .tenantId(shelfDo.getTenantId())
                                .type(StockTypeEnum.one.getValue())
                                .build()
                );
            });
        });
        //查询入库单
        OrderInputDo orderInputDo = orderInputDao.selectOne(
                Wrappers.<OrderInputDo>lambdaQuery().eq(OrderInputDo::getSourceNo, shelfDo.getInboundSourceNo())
        );
        //记录入库单日志
        operateRecordService.operateAddRecordAndValue(
                SysConstant.OrderInput_Shelf,
                "",
                orderInputDo.getId(),
                userId,
                username,
                OperateTypeEnum.OrderInput
        );
        //判断是否上架完成
        int notShelfNum = this.shelfByBoxMarkNoListPage(
                ShelfByBoxMarkNoQueryBo.builder()
                        .id(pasId)
                        .pasNo(pasNo)
                        .build()
        ).getData().size();
        if (notShelfNum == 0) {
            //修改上架单状态
            this.shelfFinishHandler(shelfDo, orderInputDo, userId, username);
            return true;
        }
        this.lambdaUpdate()
                .eq(OrderInputShelfDo::getId, pasId)
                .set(OrderInputShelfDo::getStatus, OrderInputShelfStatusEnum.one.getValue())
                .update();
        return false;
    }

    /**
     * 上架完成后续操作
     *
     * @param shelfDo  上架单
     * @param userId   操作人
     * @param username 操作人
     */
    private void shelfFinishHandler(OrderInputShelfDo shelfDo, OrderInputDo orderInputDo, Long userId, String username) {
        Long pasId = shelfDo.getId();
        DateTime shelfTime = DateUtil.date();
        //记录上架单日志
        operateRecordService.operateAddRecordAndValue(
                SysConstant.OrderInput_Shelf_Complete,
                "",
                pasId,
                userId,
                username,
                OperateTypeEnum.Shelf
        );
        //修改上架单状态为已上架
        this.lambdaUpdate()
                .eq(OrderInputShelfDo::getId, pasId)
                .set(OrderInputShelfDo::getStatus, OrderInputShelfStatusEnum.two.getValue())
                .set(OrderInputShelfDo::getPasTime, shelfTime)
                .update();
        boolean isFinish = this.checkInputShelfFinish(orderInputDo.getId());
        if (isFinish) {
            //修改入库单状态&上架时间
            OrderInputDo inputDo = new OrderInputDo();
            inputDo.setId(orderInputDo.getId());
            inputDo.setStatus(OrderInputStatusEnum.four.getValue());
            inputDo.setShelfTime(shelfTime);
            orderInputDao.updateById(inputDo);
            //记录入库单日志
            operateRecordService.operateAddRecordAndValue(
                    SysConstant.OrderInput_Shelf_Complete,
                    "",
                    orderInputDo.getId(),
                    userId,
                    username,
                    OperateTypeEnum.OrderInput,
                    DateUtil.offset(DateUtil.date(), DateField.SECOND, 3)
            );
            //修改入库单进度
            progressService.lambdaUpdate()
                    .eq(ProgressDo::getBizNumber, orderInputDo.getId())
                    .eq(ProgressDo::getType, ProgressEnum.INBOUND.getTypeCode())
                    .eq(ProgressDo::getBizType, "上架")
                    .set(ProgressDo::getBizType, "上架完成")
                    .set(ProgressDo::getOperateTime, shelfTime)
                    .set(ProgressDo::getOperateBy, username)
                    .update();
//            rabbitTemplate.convertAndSend(RabbitQueueConstant.Shelf_Real_Cost, JSON.toJSONString(orderInputDo));
            context.publishEvent(new InboundAddCostEvent(orderInputDo, 0));
        }
    }

    /**
     * 入库单是否完成上架
     *
     * @param id 入库单id
     * @return 是否完成 true:是 false:否
     */
    private boolean checkInputShelfFinish(Long id) {
        List<OrderBoxDo> boxDoList = boxService.lambdaQuery()
                .eq(OrderBoxDo::getOrderId, id)
                .select(OrderBoxDo::getId, OrderBoxDo::getOrderId, OrderBoxDo::getPasNo)
                .list();
        if (CollUtil.isNotEmpty(boxDoList)) {
            Optional<OrderBoxDo> first = boxDoList.stream().filter(i -> StrUtil.isEmpty(i.getPasNo())).findFirst();
            if (first.isPresent()) {
                return false;
            }
            return this.lambdaQuery()
                           .in(OrderInputShelfDo::getPasNo, boxDoList.stream().map(OrderBoxDo::getPasNo).collect(Collectors.toSet()))
                           .ne(OrderInputShelfDo::getStatus, OrderInputShelfStatusEnum.two.getValue())
                           .count() == 0;
        }
        return false;
    }

    @Override
    public OrderInputCostReq buildInboundCostReq(OrderInputDo orderInputDo, Integer status) {
        OrderInputCostReq req = new OrderInputCostReq();
        req.setStatus(status);
        req.setOrderType(StaticDict.Cost_Input_OrderType.getOrderType(orderInputDo.getInboundType()));
        req.setOrderNo(orderInputDo.getSourceNo());
        req.setOrderId(orderInputDo.getId());
        req.setWhId(orderInputDo.getWarehouseId());
        req.setCompanyId(orderInputDo.getCompanyId());
        req.setTenantId(orderInputDo.getTenantId());
        req.setOmsOrderCreateTime(orderInputDo.getCreateTime());
        Integer inboundType = orderInputDo.getInboundType();
        List<OrderBoxDo> boxDoList = boxService.lambdaQuery()
                .eq(OrderBoxDo::getOrderId, orderInputDo.getId())
                .list();
        switch (inboundType) {
            case 1:
                //相同sku合并
                Map<Long, OrderBoxProductDo> boxProductDoMap = boxProductService.lambdaQuery()
                        .in(OrderBoxProductDo::getBoxMarkNumber, boxDoList.stream().map(OrderBoxDo::getBoxMarkNumber).collect(Collectors.toList()))
                        .list().stream().collect(Collectors.toMap(OrderBoxProductDo::getProductId, val -> val, (o1, o2) -> {
                            o1.setReceiptQty(o1.getReceiptQty() + o2.getReceiptQty());
                            o1.setPerBoxQty(o1.getPerBoxQty() + o2.getPerBoxQty());
                            return o1;
                        }));
                //sku查产品信息
                Map<Long, ProductDo> productIdMatch = productService.lambdaQuery()
                        .in(ProductDo::getId, boxProductDoMap.keySet())
                        .list().stream().collect(Collectors.toMap(ProductDo::getId, val -> val));
                List<SkuMsg> skus1 = new ArrayList<>(boxProductDoMap.size());
                for (Map.Entry<Long, OrderBoxProductDo> boxProductDoEntry : boxProductDoMap.entrySet()) {
                    Long productId = boxProductDoEntry.getKey();
                    ProductDo productDo = productIdMatch.get(productId);
                    OrderBoxProductDo boxProductDo = boxProductDoEntry.getValue();
//                    Size size = new Size();
//                    BeanUtils.copyProperties(productDo, size);
                    Size size = BeanConvert.INSTANCE.productDoToSize(productDo);
                    skus1.add(
                            SkuMsg.builder()
                                    .size(size)
                                    .qty(Objects.equals(boxProductDo.getReceiptQty(), 0) ? boxProductDo.getPerBoxQty() : boxProductDo.getReceiptQty())
                                    .productId(productDo.getId())
                                    .build()
                    );
                }
                req.setSkus(skus1);
                List<BoxMsg> boxes1 = new ArrayList<>(boxDoList.size());
                for (OrderBoxDo boxDo : boxDoList) {
//                    Size size = new Size();
//                    BeanUtils.copyProperties(boxDo, size);
                    Size size = BeanConvert.INSTANCE.orderBoxDoToSize(boxDo);
                    boxes1.add(
                            BoxMsg.builder()
                                    .size(size)
                                    .qty(1)
                                    .trackNo(boxDo.getBoxMarkNumber())
                                    .build()
                    );
                }
                req.setBoxes(boxes1);
                break;
            case 2:
                //相同箱类号合并接受数量
                Map<String, OrderBoxDo> boxDoMap = boxDoList.stream().collect(Collectors.toMap(OrderBoxDo::getBoxTypeNumber, val -> val, (o1, o2) -> {
                    o1.setReceivedBoxQty(o1.getReceivedBoxQty() + o2.getReceivedBoxQty());
                    return o1;
                }));
                List<OrderBoxProductDo> bpList = boxProductService.lambdaQuery()
                        .in(OrderBoxProductDo::getBoxTypeNumber, boxDoList.stream().map(OrderBoxDo::getBoxTypeNumber).collect(Collectors.toSet()))
                        .list();
                //sku乘箱数
                for (OrderBoxProductDo boxProductDo : bpList) {
                    OrderBoxDo boxDo = boxDoMap.get(boxProductDo.getBoxTypeNumber());
                    Integer qty = Objects.equals(boxDo.getReceivedBoxQty(), 0) ? boxDo.getBoxTotalQty() : boxDo.getReceivedBoxQty();
                    boxProductDo.setPerBoxQty(boxProductDo.getPerBoxQty() * qty);
                    boxProductDo.setReceiptQty(boxProductDo.getReceiptQty() * qty);
                }
                //相同sku合并
                Map<Long, OrderBoxProductDo> boxProductDoMap2 = bpList.stream().collect(Collectors.toMap(OrderBoxProductDo::getProductId, val -> val, (o1, o2) -> {
                    o1.setReceiptQty(o1.getReceiptQty() + o2.getReceiptQty());
                    o1.setPerBoxQty(o1.getPerBoxQty() + o2.getPerBoxQty());
                    return o1;
                }));
                //sku查产品信息
                Map<Long, ProductDo> productIdMatch2 = productService.lambdaQuery()
                        .in(ProductDo::getId, boxProductDoMap2.keySet())
                        .list().stream().collect(Collectors.toMap(ProductDo::getId, val -> val));
                List<SkuMsg> skus2 = new ArrayList<>(boxProductDoMap2.size());
                for (Map.Entry<Long, OrderBoxProductDo> boxProductDoEntry : boxProductDoMap2.entrySet()) {
                    Long productId = boxProductDoEntry.getKey();
                    ProductDo productDo = productIdMatch2.get(productId);
                    OrderBoxProductDo boxProductDo = boxProductDoEntry.getValue();
//                    Size size = new Size();
//                    BeanUtils.copyProperties(productDo, size);
                    Size size = BeanConvert.INSTANCE.productDoToSize(productDo);
                    skus2.add(
                            SkuMsg.builder()
                                    .size(size)
                                    .qty(Objects.equals(boxProductDo.getReceiptQty(), 0) ? boxProductDo.getPerBoxQty() : boxProductDo.getReceiptQty())
                                    .productId(productDo.getId())
                                    .build()
                    );
                }
                req.setSkus(skus2);
                List<BoxMsg> boxes2 = new ArrayList<>(boxDoMap.size());
                for (Map.Entry<String, OrderBoxDo> boxDoEntry : boxDoMap.entrySet()) {
                    String boxTypeNumber = boxDoEntry.getKey();
                    OrderBoxDo boxDo = boxDoEntry.getValue();
//                    Size size = new Size();
//                    BeanUtils.copyProperties(boxDo, size);
                    Size size = BeanConvert.INSTANCE.orderBoxDoToSize(boxDo);
                    boxes2.add(
                            BoxMsg.builder()
                                    .size(size)
                                    .qty(Objects.equals(boxDo.getReceivedBoxQty(), 0) ? boxDo.getBoxTotalQty() : boxDo.getReceivedBoxQty())
                                    .trackNo(boxTypeNumber)
                                    .build()
                    );
                }
                req.setBoxes(boxes2);
                break;
            default:
        }
        return req;
    }

    @Override
    public void shelfPrint(Long pasId, HttpServletResponse response) throws IOException {
        response.setHeader("Content-Type", "application/pdf");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment;filename=shelfPrint.pdf");
        OrderInputShelfDo shelfDo = this.getById(pasId);
        String pasNo = shelfDo.getPasNo();
        //上架单号条形
        byte[] pasNoBarcode = BarCodeUtils.generateBarCode128(
                pasNo,
                true,
                false,
                256,
                15,
                0.3f,
                0f,
                0f
        );
        ByteArrayInputStream pasNoBarcodeImg = new ByteArrayInputStream(pasNoBarcode);
        //获取客户信息
        CompanyDo companyDo = companyService.getById(shelfDo.getCompanyId());
        String company = companyDo.getName() + "(" + companyDo.getCompanyCode() + ")";
        //通用参数
        Map<String, Object> data = new HashMap<>(16);
        data.put("pasNo", pasNo);
        data.put("inboundNo", shelfDo.getInboundSourceNo());
        data.put("productType", "Standard");
        data.put("company", company);
        data.put("printTime", DateUtil.date());
        ConfigureBuilder configureBuilder = Configure.builder();
        XWPFTemplate template;
        ByteArrayOutputStream pdf;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ServletOutputStream out = response.getOutputStream();
        List<OrderBoxDo> boxDoList = boxService.lambdaQuery()
                .eq(OrderBoxDo::getPasNo, pasNo)
                .list();
        //入库类型
        Integer inboundType = shelfDo.getInboundType();
        switch (inboundType) {
            case 1:
                data.put("pasNoBarcode", Pictures.ofStream(pasNoBarcodeImg, PictureType.PNG).size(245, 70).create());
                List<OrderBoxProductDo> bpList1 = boxProductService.lambdaQuery()
                        .in(OrderBoxProductDo::getBoxMarkNumber, boxDoList.stream().map(OrderBoxDo::getBoxMarkNumber).collect(Collectors.toList()))
                        .list();
                Map<Long, ProductDo> productIdMatch1 = productService.lambdaQuery()
                        .in(ProductDo::getId, bpList1.stream().map(OrderBoxProductDo::getProductId).collect(Collectors.toList()))
                        .list().stream().collect(Collectors.toMap(ProductDo::getId, val -> val));
                //按产品查库存
                List<ProductStockDo> stockDoList1 = productStockService.lambdaQuery()
                        .eq(ProductStockDo::getOrderId, shelfDo.getId())
                        .eq(ProductStockDo::getOrderType, StockOrderTypeEnum.Shelf.getValue())
                        .eq(ProductStockDo::getType, StockTypeEnum.one.getValue())
                        .list();
                List<ProductStockRackDo> stockRackDoList1 = productStockRackService.lambdaQuery()
                        .in(CollUtil.isNotEmpty(stockDoList1), ProductStockRackDo::getStockId, stockDoList1.stream().map(ProductStockDo::getId).collect(Collectors.toList()))
                        .list();
                Map<Long, StorageLocationDo> rackIdMatch = storageLocationService.lambdaQuery()
                        .in(CollUtil.isNotEmpty(stockRackDoList1), StorageLocationDo::getId, stockRackDoList1.stream().map(ProductStockRackDo::getRackId).collect(Collectors.toList()))
                        .list().stream().collect(Collectors.toMap(StorageLocationDo::getId, val -> val));
                Collection<OrderBoxProductDo> bpListByProductIdGroup = bpList1.stream().collect(Collectors.toMap(OrderBoxProductDo::getProductId, val -> val, (o1, o2) -> {
                    o1.setPerBoxQty(o1.getPerBoxQty() + o2.getPerBoxQty());
                    o1.setReceiptQty(o1.getReceiptQty() + o2.getReceiptQty());
                    return o1;
                })).values();
                data.put("skuKind", bpListByProductIdGroup.size());
                data.put("skuTotalQty", bpListByProductIdGroup.stream().mapToInt(OrderBoxProductDo::getReceiptQty).sum());
                List<NormalShelfTableDto> normalShelfTableDtoList1 = new ArrayList<>();
                AtomicInteger i = new AtomicInteger(1);
                for (OrderBoxProductDo bpDo : bpListByProductIdGroup) {
                    NormalShelfTableDto dto = new NormalShelfTableDto();
                    ProductDo productDo = productIdMatch1.get(bpDo.getProductId());
                    dto.setNo(String.valueOf(i.getAndIncrement()));
                    dto.setSku(bpDo.getSku());
                    dto.setJanCode(Objects.nonNull(productDo) ? productDo.getJanCode() : "-");
                    dto.setProductName(Objects.nonNull(productDo) ? productDo.getProductName() : "-");
                    dto.setQty(String.valueOf(bpDo.getReceiptQty()));
                    if (CollUtil.isNotEmpty(stockDoList1)) {
                        //按产品查库存
                        List<ProductStockDo> productStockDoListByProductId = stockDoList1.stream().filter(item -> Objects.equals(item.getProductId(), bpDo.getProductId())).collect(Collectors.toList());
                        List<Long> stockIdList1 = productStockDoListByProductId.stream().map(ProductStockDo::getId).collect(Collectors.toList());
                        //库存按库位合并
                        Collection<ProductStockRackDo> stockRackListByStockId = stockRackDoList1.stream()
                                .filter(item -> stockIdList1.contains(item.getStockId()))
                                .collect(Collectors.toMap(ProductStockRackDo::getRackId, val -> val, (o1, o2) -> {
                                    o1.setReceivedQty(o1.getReceivedQty() + o2.getReceivedQty());
                                    return o1;
                                })).values();
                        if (CollUtil.isNotEmpty(stockRackListByStockId)) {
                            for (ProductStockRackDo stockRackDo : stockRackListByStockId) {
                                StorageLocationDo rackDo = rackIdMatch.get(stockRackDo.getRackId());
                                HashMap<String, String> map = new HashMap<>(4);
                                map.put("cellCode", rackDo.getCode());
                                map.put("cellQty", String.valueOf(stockRackDo.getReceivedQty()));
                                dto.getSonList().add(map);
                            }
                            int shelfNum = stockRackListByStockId.stream().mapToInt(ProductStockRackDo::getReceivedQty).sum();
                            if (shelfNum != bpDo.getReceiptQty()) {
                                HashMap<String, String> map = new HashMap<>(4);
                                map.put("cellCode", "Not on the shelf");
                                map.put("cellQty", String.valueOf(bpDo.getReceiptQty() - shelfNum));
                                dto.getSonList().add(map);
                            }
                        }
                    }
                    if (CollUtil.isEmpty(dto.getSonList())) {
                        HashMap<String, String> map = new HashMap<>(4);
                        map.put("cellCode", "Not on the shelf");
                        map.put("cellQty", String.valueOf(bpDo.getReceiptQty()));
                        dto.getSonList().add(map);
                    }
                    normalShelfTableDtoList1.add(dto);
                }
                data.put("table",
                        this.getNormalShelfPrintTableData(
                                normalShelfTableDtoList1,
                                new DynamicTableDto(0, 0, 4, 7, 1)
                        )
                );
                //模板配置
                configureBuilder.bind("table", new DynamicTablePolicy());
                template = XWPFTemplate.compile(this.getClass().getClassLoader().getResourceAsStream("wordTemplates/shelf-normal.docx"), configureBuilder.build()).render(data);
                template.writeAndClose(bos);
                //转换为pdf
                pdf = AsposePdfUtils.word2pdf(new ByteArrayInputStream(bos.toByteArray()));
                out.write(pdf.toByteArray());
                bos.flush();
                out.flush();
                PoitlIOUtils.closeQuietlyMulti(template, bos, out, pdf);
                break;
            case 2:
                data.put("pasNoBarcode", Pictures.ofStream(pasNoBarcodeImg, PictureType.PNG).size(200, 60).create());
                //按箱查库存
                List<ProductStockDo> stockDoList2 = productStockService.lambdaQuery()
                        .eq(ProductStockDo::getOrderId, shelfDo.getId())
                        .eq(ProductStockDo::getOrderType, StockOrderTypeEnum.Shelf.getValue())
                        .eq(ProductStockDo::getType, StockTypeEnum.two.getValue())
                        .list();
                List<ProductStockRackDo> stockRackDoList2 = productStockRackService.lambdaQuery()
                        .in(CollUtil.isNotEmpty(stockDoList2), ProductStockRackDo::getStockId, stockDoList2.stream().map(ProductStockDo::getId).collect(Collectors.toList()))
                        .list();
                Map<Long, StorageLocationDo> rackIdMatch2 = storageLocationService.lambdaQuery()
                        .in(CollUtil.isNotEmpty(stockRackDoList2), StorageLocationDo::getId, stockRackDoList2.stream().map(ProductStockRackDo::getRackId).collect(Collectors.toList()))
                        .list().stream().collect(Collectors.toMap(StorageLocationDo::getId, val -> val));
                data.put("boxKind", boxDoList.size());
                data.put("boxTotalQty", boxDoList.stream().mapToInt(OrderBoxDo::getReceivedBoxQty).sum());
                AtomicInteger j = new AtomicInteger(1);
                List<TransferShelfTableDto> dtoList2 = new ArrayList<>();
                for (OrderBoxDo boxDo : boxDoList) {
                    TransferShelfTableDto dto = new TransferShelfTableDto();
                    dto.setNo(String.valueOf(j.getAndIncrement()));
                    dto.setBoxTypeNumber(boxDo.getBoxTypeNumber());
                    dto.setQty(String.valueOf(boxDo.getReceivedBoxQty()));
                    if (CollUtil.isNotEmpty(stockDoList2)) {
                        //按箱查库存
                        List<ProductStockDo> stockListByBox = stockDoList2.stream().filter(item -> item.getBoxTypeNumber().equals(boxDo.getBoxTypeNumber())).collect(Collectors.toList());
                        List<Long> stockIdList2 = stockListByBox.stream().map(ProductStockDo::getId).collect(Collectors.toList());
                        //库存按库位合并
                        Collection<ProductStockRackDo> stockRackListByStockId = stockRackDoList2.stream()
                                .filter(item -> stockIdList2.contains(item.getStockId()))
                                .collect(Collectors.toMap(ProductStockRackDo::getRackId, val -> val, (o1, o2) -> {
                                    o1.setReceivedQty(o1.getReceivedQty() + o2.getReceivedQty());
                                    return o1;
                                })).values();
                        if (CollUtil.isNotEmpty(stockRackListByStockId)) {
                            for (ProductStockRackDo stockRackDo : stockRackListByStockId) {
                                StorageLocationDo rackDo = rackIdMatch2.get(stockRackDo.getRackId());
                                HashMap<String, String> map = new HashMap<>(4);
                                map.put("cellCode", rackDo.getCode());
                                map.put("cellQty", String.valueOf(stockRackDo.getReceivedQty()));
                                dto.getSonList().add(map);
                            }
                            int shelfNum = stockRackListByStockId.stream().mapToInt(ProductStockRackDo::getReceivedQty).sum();
                            if (shelfNum != boxDo.getReceivedBoxQty()) {
                                HashMap<String, String> map = new HashMap<>(4);
                                map.put("cellCode", "Not on the shelf");
                                map.put("cellQty", String.valueOf(boxDo.getReceivedBoxQty() - shelfNum));
                                dto.getSonList().add(map);
                            }
                        }
                    }
                    if (CollUtil.isEmpty(dto.getSonList())) {
                        HashMap<String, String> map = new HashMap<>(4);
                        map.put("cellCode", "Not on the shelf");
                        map.put("cellQty", String.valueOf(boxDo.getReceivedBoxQty()));
                        dto.getSonList().add(map);
                    }
                    dtoList2.add(dto);
                }
                data.put("table",
                        this.getTransferShelfPrintTableData(
                                dtoList2,
                                new DynamicTableDto(0, 0, 2, 5, 1)
                        )
                );
                //模板配置
                configureBuilder.bind("table", new DynamicTablePolicy());
                template = XWPFTemplate.compile(this.getClass().getClassLoader().getResourceAsStream("wordTemplates/shelf-transfer.docx"), configureBuilder.build()).render(data);
                template.writeAndClose(bos);
                //转换为pdf
                pdf = AsposePdfUtils.word2pdf(new ByteArrayInputStream(bos.toByteArray()));
                out.write(pdf.toByteArray());
                bos.flush();
                out.flush();
                PoitlIOUtils.closeQuietlyMulti(template, bos, out, pdf);
                break;
            default:
        }
    }

    @Override
    public void wholeOrderReceiptAndShelf(InboundReceiptAndShelfApiBo apiBo, Long userId, String username) {
        //校验参数
        if (StrUtil.isBlank(apiBo.getSourceNo())) {
            throw new BizException(SysConstant.SourceNo_Not_Null);
        }
        //查询入库单
        OrderInputDo orderInputDo = orderInputDao.selectOne(
                Wrappers.<OrderInputDo>lambdaQuery().eq(OrderInputDo::getSourceNo, apiBo.getSourceNo())
        );
        if (Objects.isNull(orderInputDo)) {
            throw new BizException(SysConstant.Inbound_Not_Exist);
        }
        //只处理待入库的入库单
        // 如需处理操作过的入库单，需要处理这个入库单对应的所有上架单、判断上架数量、上架中的需要按照之前上架的类型继续上架
        if (!OrderInputStatusEnum.one.getValue().equals(orderInputDo.getStatus())) {
            operateRecordService.operateAddRecordAndValue(
                    SysConstant.OrderInput_Auto_Shelf_Status_Error,
                    "",
                    orderInputDo.getId(),
                    userId,
                    username,
                    OperateTypeEnum.OrderInput
            );
            throw new BizException(SysConstant.OrderInput_Auto_Shelf_Status_Error);
        }
        //1.整单收货生成上架单号
        String shelfNo = orderInputService.receiptTotalHandler(orderInputDo.getId(), userId, username, orderInputDo);
        OrderInputShelfDo shelfDo = this.getOne(Wrappers.<OrderInputShelfDo>lambdaQuery().eq(OrderInputShelfDo::getPasNo, shelfNo));
        if (shelfDo == null) {
            operateRecordService.operateAddRecordAndValue(
                    SysConstant.Operate_Failure,
                    "",
                    orderInputDo.getId(),
                    userId,
                    username,
                    OperateTypeEnum.OrderInput
            );
            throw new BizException(SysConstant.Operate_Failure);
        }
        //入库类型
        Integer inboundType = orderInputDo.getInboundType();
        Long cellId;
        StorageLocationQueryBo slBo = new StorageLocationQueryBo();
        slBo.setTenantId(orderInputDo.getTenantId());
        slBo.setWhId(orderInputDo.getWarehouseId());
        slBo.setStockAttribute(Objects.nonNull(apiBo.getStockAttribute()) ? apiBo.getStockAttribute() : 0);
        Collection<StorageLocationVo> cellList = storageLocationService.getListPage(slBo).getData();
        if (CollUtil.isEmpty(cellList)) {
            operateRecordService.operateAddRecordAndValue(
                    SysConstant.Operate_Failure,
                    "",
                    orderInputDo.getId(),
                    userId,
                    username,
                    OperateTypeEnum.OrderInput
            );
            throw new BizException(SysConstant.Operate_Failure);
        }
        //从库位中取一个
        cellId = cellList.iterator().next().getId();
        //2.根据上架单号查到需要上架的商品
        List<OrderBoxDo> boxDoList = boxService.lambdaQuery()
                .eq(OrderBoxDo::getPasNo, shelfNo)
                .list();
        //3.根据上架类型构建需要的数据
        //4.调用不同的上架方法
        if (InboundTypeEnum.one.getValue().equals(inboundType)) {
            Collection<OrderBoxProductDo> boxProductList = boxProductService.lambdaQuery()
                    .in(OrderBoxProductDo::getBoxMarkNumber, boxDoList.stream().map(OrderBoxDo::getBoxMarkNumber).collect(Collectors.toList()))
                    .list().stream().collect(Collectors.toMap(OrderBoxProductDo::getProductId, val -> val, (o1, o2) -> {
                        o1.setReceiptQty(o1.getReceiptQty() + o2.getReceiptQty());
                        return o1;
                    })).values();
            List<ShelfBySkuAddBo.SkuDetail> detailList = new ArrayList<>();
            for (OrderBoxProductDo boxProductDo : boxProductList) {
                ShelfBySkuAddBo.SkuDetail detail = new ShelfBySkuAddBo.SkuDetail();
                detail.setProductId(boxProductDo.getProductId());
                detail.setSku(boxProductDo.getSku());
                detail.setShouldQty(boxProductDo.getReceiptQty());
                detail.setShelfQtyList(Collections.singletonList(ShelfQtyDetailBo.builder().qty(boxProductDo.getReceiptQty()).cellId(cellId).build()));
                detailList.add(detail);
            }
            ShelfBySkuAddBo bo = new ShelfBySkuAddBo();
            bo.setId(shelfDo.getId());
            bo.setPasNo(shelfNo);
            bo.setDetailList(detailList);
            shelfBySku(bo, userId, username);
        } else if (InboundTypeEnum.two.getValue().equals(inboundType)) {
            List<ShelfByBoxTypeNumberAddBo.BoxTypeNumberDetail> detailList = new ArrayList<>();
            for (OrderBoxDo boxDo : boxDoList.stream().collect(Collectors.toMap(OrderBoxDo::getBoxTypeNumber, val -> val, (o1, o2) -> {
                o1.setReceivedBoxQty(o1.getReceivedBoxQty() + o2.getReceivedBoxQty());
                return o1;
            })).values()) {
                ShelfByBoxTypeNumberAddBo.BoxTypeNumberDetail detail = new ShelfByBoxTypeNumberAddBo.BoxTypeNumberDetail();
                detail.setBoxTypeNumber(boxDo.getBoxTypeNumber());
                detail.setShouldQty(boxDo.getReceivedBoxQty());
                detail.setShelfQtyList(Collections.singletonList(ShelfQtyDetailBo.builder().cellId(cellId).qty(boxDo.getReceivedBoxQty()).build()));
                detailList.add(detail);
            }
            ShelfByBoxTypeNumberAddBo bo = new ShelfByBoxTypeNumberAddBo();
            bo.setId(shelfDo.getId());
            bo.setPasNo(shelfNo);
            bo.setDetailList(detailList);
            shelfByBoxTypeNo(bo, userId, username);
        }
    }

    private DynamicTableDto getTransferShelfPrintTableData(List<TransferShelfTableDto> dtoList, DynamicTableDto dynamicTableDto) {
        for (TransferShelfTableDto dto : dtoList) {
            dynamicTableDto.getGroupMap().put(dto.getNo(), dto.getSonList().size());
            //箱类号条形码
            byte[] boxBarcode = BarCodeUtils.generateBarCode128(
                    dto.getBoxTypeNumber(),
                    false,
                    false,
                    256,
                    15,
                    0.4f,
                    0,
                    0
            );
            ByteArrayInputStream boxBarcodeImg = new ByteArrayInputStream(boxBarcode);
            for (Map<String, String> sonMap : dto.getSonList()) {
                RowRenderData rowRenderData = Rows.of(
                        Cells.of(dto.getNo()).create(),
                        Cells.of(Pictures.ofStream(boxBarcodeImg, PictureType.PNG).size(200, 55).create()).create(),
                        Cells.of(dto.getQty()).create(),
                        Cells.of(sonMap.get("cellCode")).create(),
                        Cells.of(sonMap.get("cellQty")).create()
                ).center().create();
                dynamicTableDto.getDataList().add(rowRenderData);
            }
        }
        return dynamicTableDto;
    }

    private DynamicTableDto getNormalShelfPrintTableData(List<NormalShelfTableDto> dtoList, DynamicTableDto dynamicTableDto) {
        for (NormalShelfTableDto dto : dtoList) {
            dynamicTableDto.getGroupMap().put(dto.getNo(), dto.getSonList().size());
            for (Map<String, String> sonMap : dto.getSonList()) {
                RowRenderData rowRenderData = Rows.of(
                        Cells.of(dto.getNo()).create(),
                        Cells.of(dto.getSku()).create(),
                        Cells.of(dto.getJanCode()).create(),
                        Cells.of(dto.getProductName()).create(),
                        Cells.of(dto.getQty()).create(),
                        Cells.of(sonMap.get("cellCode")).create(),
                        Cells.of(sonMap.get("cellQty")).create()
                ).center().create();
                dynamicTableDto.getDataList().add(rowRenderData);
            }
        }
        return dynamicTableDto;
    }

    @Override
    public boolean shelfBySku(ShelfBySkuAddBo bo, Long userId, String username) {
        //上架id&上架单号
        Long pasId = bo.getId();
        String pasNo = bo.getPasNo();
        List<ShelfBySkuAddBo.SkuDetail> detailList = bo.getDetailList();
        if (CollUtil.isEmpty(detailList)) {
            throw new BizException(SysConstant.Please_Select_Shelf_Data);
        }
        boolean anyMatch = bo.getDetailList().stream()
                .anyMatch(item -> item.getShouldQty() <
                                  item.getShelfQtyList().stream().map(ShelfQtyDetailBo::getQty).reduce(0, Integer::sum));
        if (anyMatch) {
            throw new BizException(SysConstant.Shelf_Qty_No_Match);
        }
        //查询上架单
        OrderInputShelfDo shelfDo = this.getById(pasId);
        //查询库位信息 用于匹配库存属性
        StorageLocationQueryBo slBo = new StorageLocationQueryBo();
        Set<Long> cellIdList = new HashSet<>();
        detailList.stream()
                .map(ShelfBySkuAddBo.SkuDetail::getShelfQtyList)
                .forEach(shelfQtyList ->
                        cellIdList.addAll(
                                shelfQtyList.stream()
                                        .map(ShelfQtyDetailBo::getCellId)
                                        .collect(Collectors.toList())
                        )
                );
        slBo.setIdList(cellIdList);
        slBo.setTenantId(shelfDo.getTenantId());
        slBo.setPageSize(1000);
        //准备需要的数据 库位信息 产品信息
        Map<Long, StorageLocationVo> cellIdMatchDo = storageLocationService.getListPage(slBo).getData()
                .stream().collect(Collectors.toMap(StorageLocationVo::getId, val -> val));
        Map<Long, ProductDo> productIdMatchDo = productService.lambdaQuery()
                .in(ProductDo::getId, detailList.stream().map(ShelfBySkuAddBo.SkuDetail::getProductId).collect(Collectors.toList()))
                .list().stream().collect(Collectors.toMap(ProductDo::getId, val -> val));
        detailList.forEach(item -> {
            //组装数据插入主表
            ProductStockDo productStockDo = new ProductStockDo();
            productStockDo.setWhId(shelfDo.getWhId());
            productStockDo.setCompanyId(shelfDo.getCompanyId());
            productStockDo.setOrderType(StockOrderTypeEnum.Shelf.getValue());
            productStockDo.setOrderId(pasId);
            productStockDo.setType(StockTypeEnum.one.getValue());
            productStockDo.setProductId(productIdMatchDo.get(item.getProductId()).getId());
            productStockDo.setReceiveQuantity(item.getShelfQtyList().stream()
                    .map(ShelfQtyDetailBo::getQty).reduce(0, Integer::sum));
            productStockService.save(productStockDo);
            //组装数据插入子表
            //合并相同库位
            item.getShelfQtyList().stream().collect(Collectors.toMap(ShelfQtyDetailBo::getCellId, val -> val, (v1, v2) -> {
                v1.setQty(v1.getQty() + v2.getQty());
                return v1;
            })).values().forEach(shelfQtyDetailBo -> {
                ProductStockRackDo productStockRackDo = new ProductStockRackDo();
                productStockRackDo.setStockId(productStockDo.getId());
                productStockRackDo.setRackId(shelfQtyDetailBo.getCellId());
                productStockRackDo.setStockAttribute(cellIdMatchDo.get(shelfQtyDetailBo.getCellId()).getMainType());
                productStockRackDo.setReceivedQty(shelfQtyDetailBo.getQty());
                productStockRackDo.setQuantity(shelfQtyDetailBo.getQty());
                productStockRackService.save(productStockRackDo);
                //库存流水
                stockFlowingService.insertProForIn(
                        ProductFlowingInEt.builder()
                                .sku(item.getSku())
                                .companyId(shelfDo.getCompanyId())
                                .warehouseId(productStockDo.getWhId())
                                .rackId(shelfQtyDetailBo.getCellId())
                                .stockAttribute(cellIdMatchDo.get(shelfQtyDetailBo.getCellId()).getMainType())
                                .bookQuantity(shelfQtyDetailBo.getQty())
                                .orderType(StockOrderTypeEnum.Shelf.getValue())
                                .noteNo(shelfDo.getPasNo())
                                .tenantId(shelfDo.getTenantId())
                                .type(StockTypeEnum.one.getValue())
                                .build()
                );
            });
        });
        //查询入库单
        OrderInputDo orderInputDo = orderInputDao.selectOne(
                Wrappers.<OrderInputDo>lambdaQuery().eq(OrderInputDo::getSourceNo, shelfDo.getInboundSourceNo())
        );
        if (Objects.equals(Boolean.TRUE, orderInputDo.getApiSkuAcceptFlag()) && !bo.isJdApi()) {
            throw new BizException(SysConstant.OrderInput_Api_Sku_Accept_Error);
        }
        //记录入库单日志
        operateRecordService.operateAddRecordAndValue(
                SysConstant.OrderInput_Shelf,
                "",
                orderInputDo.getId(),
                userId,
                username,
                OperateTypeEnum.OrderInput
        );
        //查询未上架数量
        long notShelfNum = this.shelfBySkuListPage(
                ShelfBySkuQueryBo.builder()
                        .id(pasId)
                        .pasNo(pasNo)
                        .build()
        ).getData().size();
        // 对外收货上架接口直接收货并完成上架
        if (bo.isJdApi() || notShelfNum == 0) {
            //修改上架单状态
            this.shelfFinishHandler(shelfDo, orderInputDo, userId, username);
            return true;
        }
        this.lambdaUpdate()
                .eq(OrderInputShelfDo::getId, pasId)
                .set(OrderInputShelfDo::getStatus, OrderInputShelfStatusEnum.one.getValue())
                .update();
        return false;
    }

    @Override
    public boolean shelfByBoxTypeNo(ShelfByBoxTypeNumberAddBo bo, Long userId, String username) {
        //上架id&上架单号
        Long pasId = bo.getId();
        String pasNo = bo.getPasNo();
        List<ShelfByBoxTypeNumberAddBo.BoxTypeNumberDetail> detailList = bo.getDetailList();
        if (CollUtil.isEmpty(detailList)) {
            throw new BizException(SysConstant.Please_Select_Shelf_Data);
        }
        boolean anyMatch = bo.getDetailList().stream()
                .anyMatch(item -> item.getShouldQty() <
                                  item.getShelfQtyList().stream().map(ShelfQtyDetailBo::getQty).reduce(0, Integer::sum));
        if (anyMatch) {
            throw new BizException(SysConstant.Shelf_Qty_No_Match);
        }
        //查询上架单
        OrderInputShelfDo shelfDo = this.getById(pasId);
        //查询库位信息 用于匹配库存属性
        StorageLocationQueryBo slBo = new StorageLocationQueryBo();
        Set<Long> cellIdList = new HashSet<>();
        detailList.stream()
                .map(ShelfByBoxTypeNumberAddBo.BoxTypeNumberDetail::getShelfQtyList)
                .forEach(shelfQtyList ->
                        cellIdList.addAll(
                                shelfQtyList.stream()
                                        .map(ShelfQtyDetailBo::getCellId)
                                        .collect(Collectors.toList())
                        )
                );
        slBo.setIdList(cellIdList);
        slBo.setTenantId(shelfDo.getTenantId());
        slBo.setPageSize(1000);
        //准备需要的数据 库位信息 产品信息
        Map<Long, StorageLocationVo> cellIdMatchDo = storageLocationService.getListPage(slBo).getData()
                .stream().collect(Collectors.toMap(StorageLocationVo::getId, val -> val));
        detailList.forEach(item -> {
            //组装数据插入主表
            ProductStockDo productStockDo = new ProductStockDo();
            productStockDo.setCompanyId(shelfDo.getCompanyId());
            productStockDo.setOrderType(StockOrderTypeEnum.Shelf.getValue());
            productStockDo.setOrderId(pasId);
            productStockDo.setBoxTypeNumber(item.getBoxTypeNumber());
            productStockDo.setType(StockTypeEnum.two.getValue());
            //接收数量计算
            productStockDo.setReceiveQuantity(
                    item.getShelfQtyList().stream()
                            .map(ShelfQtyDetailBo::getQty)
                            .reduce(0, Integer::sum)
            );
            productStockService.save(productStockDo);
            //组装数据插入子表
            //合并相同库位
            item.getShelfQtyList().stream().collect(Collectors.toMap(ShelfQtyDetailBo::getCellId, val -> val, (v1, v2) -> {
                v1.setQty(v1.getQty() + v2.getQty());
                return v1;
            })).values().forEach(shelfQtyDetailBo -> {
                ProductStockRackDo productStockRackDo = new ProductStockRackDo();
                productStockRackDo.setStockId(productStockDo.getId());
                productStockRackDo.setRackId(shelfQtyDetailBo.getCellId());
                productStockRackDo.setStockAttribute(cellIdMatchDo.get(shelfQtyDetailBo.getCellId()).getMainType());
                productStockRackDo.setReceivedQty(shelfQtyDetailBo.getQty());
                productStockRackDo.setQuantity(shelfQtyDetailBo.getQty());
                productStockRackService.save(productStockRackDo);
                //库存流水
                stockFlowingService.insertProForIn(
                        ProductFlowingInEt.builder()
                                .sku(item.getBoxTypeNumber())
                                .companyId(shelfDo.getCompanyId())
                                .warehouseId(productStockDo.getWhId())
                                .rackId(shelfQtyDetailBo.getCellId())
                                .stockAttribute(cellIdMatchDo.get(shelfQtyDetailBo.getCellId()).getMainType())
                                .bookQuantity(shelfQtyDetailBo.getQty())
                                .orderType(StockOrderTypeEnum.Shelf.getValue())
                                .noteNo(shelfDo.getPasNo())
                                .tenantId(shelfDo.getTenantId())
                                .type(StockTypeEnum.two.getValue())
                                .build()
                );
            });
        });
        //查询入库单
        OrderInputDo orderInputDo = orderInputDao.selectOne(
                Wrappers.<OrderInputDo>lambdaQuery().eq(OrderInputDo::getSourceNo, shelfDo.getInboundSourceNo())
        );
        //记录入库单日志
        operateRecordService.operateAddRecordAndValue(
                SysConstant.OrderInput_Shelf,
                "",
                orderInputDo.getId(),
                userId,
                username,
                OperateTypeEnum.OrderInput
        );
        //查询未上架数量
        int notShelfCount = this.shelfByBoxTypeNoListPage(
                ShelfByBoxTypeNumberQueryBo.builder()
                        .id(pasId)
                        .pasNo(pasNo)
                        .build()
        ).getData().size();
        if (notShelfCount == 0) {
            //修改上架单状态
            this.shelfFinishHandler(shelfDo, orderInputDo, userId, username);
            return true;
        }
        this.lambdaUpdate()
                .eq(OrderInputShelfDo::getId, pasId)
                .set(OrderInputShelfDo::getStatus, OrderInputShelfStatusEnum.one.getValue())
                .update();
        return false;
    }

    @Override
    public Integer partShelfOption(Long pasId) {
        ProductStockDo one = productStockService.lambdaQuery()
                .eq(ProductStockDo::getOrderId, pasId)
                .eq(ProductStockDo::getOrderType, StockOrderTypeEnum.Shelf.getValue())
                .last("limit 1")
                .one();
        if (Objects.isNull(one)) {
            return null;
        }
        if (Objects.nonNull(one.getBoxMarkNumber())) {
            return 2;
        }
        if (Objects.nonNull(one.getProductId())) {
            return 1;
        }
        return null;
    }

    @Override
    public SingleResult<List<Map<String, Object>>> getListPageStatus(OrderInputShelfQueryBo bo) {
        bo.setStatus(null);
        Map<Integer, Integer> data = baseMapper.getListPageStatusSta(bo).stream().collect(Collectors.toMap(ListPageStatusStaDto::getStatus, ListPageStatusStaDto::getQty));
        List<Map<String, Object>> resultList = new ArrayList<>();
        for (OrderInputShelfStatusEnum statusEnum : OrderInputShelfStatusEnum.values()) {
            Map<String, Object> map = new HashMap<>(8);
            map.put("id", statusEnum.getValue());
            map.put("name", statusEnum.getDesc());
            map.put("sum", data.getOrDefault(statusEnum.getValue(), 0));
            resultList.add(map);
        }
        return SingleResult.success(resultList);
    }
}
